/*
 * Copyright (C) 2014 Alpha, Inc.
 * This is NOT a freeware, use is subject to license terms.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cc.gehua.octopus.rpc.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author guna
 */
public class IOUtil {

    private IOUtil() {
    }
    private static final Logger LOG = LoggerFactory.getLogger(IOUtil.class);

    /* ------------------------------------------------------------------- */
    public final static String CRLF = "\015\012";

    /* ------------------------------------------------------------------- */
    public final static byte[] CRLF_BYTES = {(byte) '\015', (byte) '\012'};

    /* ------------------------------------------------------------------- */
    public static final int bufferSize = 64 * 1024;

    /* ------------------------------------------------------------------- */
    static class Job implements Runnable {

        InputStream in;
        OutputStream out;
        Reader read;
        Writer write;

        Job(InputStream in, OutputStream out) {
            this.in = in;
            this.out = out;
            this.read = null;
            this.write = null;
        }

        Job(Reader read, Writer write) {
            this.in = null;
            this.out = null;
            this.read = read;
            this.write = write;
        }

        /* ------------------------------------------------------------ */
        /* 
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            try {
                if (in != null) {
                    copy(in, out, -1);
                } else {
                    copy(read, write, -1);
                }
            } catch (IOException e) {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (write != null) {
                        write.close();
                    }
                } catch (IOException e2) {
                }
            }
        }
    }

    /* ------------------------------------------------------------------- */
    /**
     * Copy Stream in to Stream out until EOF or exception.
     * @param in
     * @param out
     * @throws java.io.IOException
     */
    public static void copy(InputStream in, OutputStream out)
            throws IOException {
        copy(in, out, -1);
    }

    /* ------------------------------------------------------------------- */
    /**
     * Copy Reader to Writer out until EOF or exception.
     * @param in
     * @param out
     * @throws java.io.IOException
     */
    public static void copy(Reader in, Writer out)
            throws IOException {
        copy(in, out, -1);
    }

    /* ------------------------------------------------------------------- */
    /**
     * Copy Stream in to Stream for byteCount bytes or until EOF or exception.
     * @param in
     * @param out
     * @param byteCount
     * @throws java.io.IOException
     */
    public static void copy(InputStream in,
            OutputStream out,
            long byteCount)
            throws IOException {
        byte buffer[] = new byte[bufferSize];
        int len;

        if (byteCount >= 0) {
            while (byteCount > 0) {
                int max = byteCount < bufferSize ? (int) byteCount : bufferSize;
                len = in.read(buffer, 0, max);

                if (len == -1) {
                    break;
                }

                byteCount -= len;
                out.write(buffer, 0, len);
            }
        } else {
            while (true) {
                len = in.read(buffer, 0, bufferSize);
                if (len < 0) {
                    break;
                }
                out.write(buffer, 0, len);
            }
        }
    }

    /* ------------------------------------------------------------------- */
    /**
     * Copy Reader to Writer for byteCount bytes or until EOF or exception.
     * @param in
     * @param out
     * @param byteCount
     * @throws java.io.IOException
     *
     */
    public static void copy(Reader in,
            Writer out,
            long byteCount)
            throws IOException {
        char buffer[] = new char[bufferSize];
        int len;

        if (byteCount >= 0) {
            while (byteCount > 0) {
                if (byteCount < bufferSize) {
                    len = in.read(buffer, 0, (int) byteCount);
                } else {
                    len = in.read(buffer, 0, bufferSize);
                }

                if (len == -1) {
                    break;
                }

                byteCount -= len;
                out.write(buffer, 0, len);
            }
        } else if (out instanceof PrintWriter) {
            PrintWriter pout = (PrintWriter) out;
            while (!pout.checkError()) {
                len = in.read(buffer, 0, bufferSize);
                if (len == -1) {
                    break;
                }
                out.write(buffer, 0, len);
            }
        } else {
            while (true) {
                len = in.read(buffer, 0, bufferSize);
                if (len == -1) {
                    break;
                }
                out.write(buffer, 0, len);
            }
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * Copy files or directories
     *
     * @param from
     * @param to
     * @throws IOException
     */
    public static void copy(File from, File to) throws IOException {
        if (from.isDirectory()) {
            copyDir(from, to);
        } else {
            copyFile(from, to);
        }
    }

    /* ------------------------------------------------------------ */
    public static void copyDir(File from, File to) throws IOException {
        if (to.exists()) {
            if (!to.isDirectory()) {
                throw new IllegalArgumentException(to.toString());
            }
        } else {
            to.mkdirs();
        }

        File[] files = from.listFiles();
        if (files != null) {
            for (File file : files) {
                String name = file.getName();
                if (".".equals(name) || "..".equals(name)) {
                    continue;
                }
                copy(file, new File(to, name));
            }
        }
    }

    /* ------------------------------------------------------------ */
    public static void copyFile(File from, File to) throws IOException {
        try (InputStream in = new FileInputStream(from);
                OutputStream out = new FileOutputStream(to)) {
            copy(in, out);
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * Read input stream to string.
     * @param in
     * @return 
     * @throws java.io.IOException 
     */
    public static String toString(InputStream in)
            throws IOException {
        return toString(in, (Charset) null);
    }

    /* ------------------------------------------------------------ */
    /**
     * Read input stream to string.
     * @param in
     * @param encoding
     * @return 
     * @throws java.io.IOException 
     */
    public static String toString(InputStream in, String encoding)
            throws IOException {
        return toString(in, encoding == null ? null : Charset.forName(encoding));
    }

    /**
     * Read input stream to string.
     *
     * @param in
     * @param encoding
     * @return 
     * @throws java.io.IOException 
     */
    
    public static String toString(InputStream in, Charset encoding)
            throws IOException {
        StringWriter writer = new StringWriter();
        InputStreamReader reader = encoding == null ? new InputStreamReader(in) : new InputStreamReader(in, encoding);

        copy(reader, writer);
        return writer.toString();
    }

    /* ------------------------------------------------------------ */
    /**
     * Read input stream to string.
     * @param in
     * @return 
     * @throws java.io.IOException
     */
    
    public static String toString(Reader in)
            throws IOException {
        StringWriter writer = new StringWriter();
        copy(in, writer);
        return writer.toString();
    }


    /* ------------------------------------------------------------ */
    /**
     * Delete File. This delete will recursively delete directories - BE
     * CAREFULL
     *
     * @param file The file to be deleted.
     * @return 
     */
    public static boolean delete(File file) {
        if (!file.exists()) {
            return false;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; files != null && i < files.length; i++) {
                delete(files[i]);
            }
        }
        return file.delete();
    }

    /* ------------------------------------------------------------ */
    /**
     * closes an input stream, and logs exceptions
     *
     * @param is the input stream to close
     */
    public static void close(InputStream is) {
        try {
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
        }
    }

    /**
     * closes a reader, and logs exceptions
     *
     * @param reader the reader to close
     */
    public static void close(Reader reader) {
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
        }
    }

    /**
     * closes a writer, and logs exceptions
     *
     * @param writer the writer to close
     */
    public static void close(Writer writer) {
        try {
            if (writer != null) {
                writer.close();
            }
        } catch (IOException e) {
        }
    }

    /* ------------------------------------------------------------ */
    public static byte[] readBytes(InputStream in)
            throws IOException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        copy(in, bout);
        return bout.toByteArray();
    }

    /* ------------------------------------------------------------ */
    /**
     * closes an output stream, and logs exceptions
     *
     * @param os the output stream to close
     */
    public static void close(OutputStream os) {
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * @return An outputstream to nowhere
     */
    public static OutputStream getNullStream() {
        return __nullStream;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return An outputstream to nowhere
     */
    public static InputStream getClosedStream() {
        return __closedStream;
    }

    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    private static class NullOS extends OutputStream {

        @Override
        public void close() {
        }

        @Override
        public void flush() {
        }

        @Override
        public void write(byte[] b) {
        }

        @Override
        public void write(byte[] b, int i, int l) {
        }

        @Override
        public void write(int b) {
        }
    }
    private static NullOS __nullStream = new NullOS();

    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    private static class ClosedIS extends InputStream {

        @Override
        public int read() throws IOException {
            return -1;
        }
    }
    private static ClosedIS __closedStream = new ClosedIS();

    /* ------------------------------------------------------------ */
    /**
     * @return An writer to nowhere
     */
    public static Writer getNullWriter() {
        return __nullWriter;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return An writer to nowhere
     */
    public static PrintWriter getNullPrintWriter() {
        return __nullPrintWriter;
    }

    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    private static class NullWrite extends Writer {

        @Override
        public void close() {
            //Do nothing
        }

        @Override
        public void flush() {
            //Do nothing
        }

        @Override
        public void write(char[] b) {
            //Do nothing
        }

        @Override
        public void write(char[] b, int o, int l) {
            //Do nothing
        }

        @Override
        public void write(int b) {
            //Do nothing
        }

        @Override
        public void write(String s) {
            //Do nothing
        }

        @Override
        public void write(String s, int o, int l) {
            //Do nothing
        }
    }
    private static final NullWrite __nullWriter = new NullWrite();
    private static  final PrintWriter __nullPrintWriter = new PrintWriter(__nullWriter);
}
